﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Devtm.Generator.Configurations;
using System.Text.RegularExpressions;
using Devtm.Generator.Contexts;

namespace Devtm.Generator
{


    [System.Diagnostics.DebuggerDisplay("{File.Name}")]
    public class Solution : IEnumerable<Project>
    {

        private Dictionary<string, object> _dic = new Dictionary<string, object>();
        protected Dictionary<string, FileInfo> _referencedProjects = new Dictionary<string, FileInfo>();
        protected Dictionary<string, Project> projects = new Dictionary<string, Project>();


        #region Store
        
        public T Store<T>(string key, Func<T> f = null)
            where T : class
        {

            T result = default(T);

            object i;
            if(_dic.TryGetValue(key, out i))
            {
                result = i as T;
            }
            else
            {
                if (f != null)
                {
                    result = f();
                    _dic[key] = result;
                }
            }

            return result;

        }

        public T Store<T>(string key)
            where T : class, new()
        {

            T result = default(T);

            object i;
            if (_dic.TryGetValue(key, out i))
            {
                result = i as T;
            }
            else
            {
                    result = new T();
                    _dic[key] = result;
            }

            return result;

        }

        public T Store<T>(string key, T o)
        where T : class
        {

            if (_dic.ContainsKey(key))
                _dic[key] = o;
            else
                _dic.Add(key, o);

            return o;

        }

        #endregion


        #region Project

        public void Saves()
        {
            foreach (var item in projects)
                item.Value.Save();
        }

        protected Project GetProject(string name)
        {
            string _name = GetConfiguration(name).ProjectFilename;
            return GetProjectByFilename(_name);
        }

        public Project GetProjectByFilename(FileInfo file)
        {
            return GetProjectByFilename(file.FullName);
        }

        public Project GetProjectByFilename(string filename)
        {

            Project prj = null;

            if (projects.ContainsKey(filename))
                prj = projects[filename];
            else
            {
                prj = new Project(GetProjectFilename(filename.ToLower())) { Solution = this };
                projects.Add(prj.ProjectFile.FullName, prj);
            }
            return prj;

        }

        public System.IO.FileInfo GetProjectFilename(string file)
        {

            if (!file.EndsWith("proj"))
                throw new FormatException("the filename's project must contains the extension");

            var l1 = _referencedProjects.Select(a => a.Value).FirstOrDefault(c => c.FullName.ToLower() == file);
            
            if (l1 != null)
                return l1;

            l1 = SolutionPath.GetFiles("*.*proj", SearchOption.AllDirectories).First(c => c.Name.ToLower() == file.ToLower());

            return l1;

        }


        public string GetExtensionFromProject(string file)
        {
            var l1 = SolutionPath.GetFiles("*.*proj", SearchOption.AllDirectories).First(c => Path.GetFileNameWithoutExtension(c.Name.ToLower()) == file.ToLower());
            return l1.Extension;
        }

        #endregion


        private GeneratorConfigurationElement GetConfiguration(string name)
        {
            return Configuration.Items[name];
        }


        public GeneratorSection Configuration { get; set; }
        public FileInfo File { get; protected set; }
        public System.IO.DirectoryInfo SolutionPath { get { return this.File.Directory; } }


        public IEnumerator<Project> GetEnumerator()
        {

            List<Project> prj = new List<Project>();
            foreach (var item in _referencedProjects)
                prj.Add(GetProjectByFilename(item.Value.FullName));

            return prj.GetEnumerator();
        
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {

            List<Project> prj = new List<Project>();
            foreach (var item in _referencedProjects)
                prj.Add(GetProjectByFilename(item.Value.FullName));

            return prj.GetEnumerator();

        }

    }

    public class Solution<TContext> : Solution
        where TContext : Context, new()
    {


        protected Dictionary<string, TContext> contexts = new Dictionary<string, TContext>();

        
        public Solution()
        {

        }


        #region Create

        /// <summary>
        /// return an object solution for a filename
        /// </summary>
        /// <param name="filename">filename or full filename</param>
        /// <returns></returns>
        public static Solution<TContext> GetSolutionObject(string filename, string configurationFile = "")
        {
            return GetSolutionObject<Solution<TContext>>(filename, configurationFile);
        }


        /// <summary>
        /// return an object solution for a filename
        /// </summary>
        /// <param name="filename">filename or full filename</param>
        /// <returns></returns>
        public static T GetSolutionObject<T>(string filename, string configurationFile = "")
            where T : Solution<TContext>, new()
        {

            T sln = new T();
            if (!string.IsNullOrEmpty(configurationFile))
            {
                System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(new System.Configuration.ExeConfigurationFileMap(configurationFile), System.Configuration.ConfigurationUserLevel.None);
                sln.Configuration = config.GetSection(GeneratorSection.DefaultName) as GeneratorSection;
            }
            else
                sln.Configuration = GeneratorSection.Configuration;


            if (System.IO.File.Exists(filename))
                sln.File = new FileInfo(filename);

            else
            {

                DirectoryInfo d = new FileInfo(typeof(Project).Assembly.Location).Directory;
                sln.File = d.GetFiles("*.sln").FirstOrDefault(c => c.Name == filename);

                while (sln.File == null)
                {
                    d = d.Parent;

                    if (d.Parent == null)
                        break;

                    else
                    {
                        sln.File = d.GetFiles("*.sln", SearchOption.AllDirectories).FirstOrDefault(c => c.Name == filename);
                    }

                }

                if (sln.File != null)
                {
                    sln.Initialize();
                }

            }

            return sln;

        }

        #endregion


        private void Initialize()
        {

            string doc = string.Empty;
            using (var r = File.OpenRead())
            {
                int lenght = (int)File.Length;
                byte[] ar = new byte[lenght];

                r.Read(ar, 0, lenght);

                doc = System.Text.Encoding.Default.GetString(ar);

            }

            Regex _reg = new Regex(@"Project\(""{[\w\d-]+}""\) = (""[\w\d_.]*""), ""([\w\d_.\\]*)"", ""{[\w\d-]+}""");
            foreach (Match item in _reg.Matches(doc))
            {
                var g1 = item.Groups[1];
                var g2 = item.Groups[2];
                FileInfo f1 = new FileInfo(Path.Combine(File.Directory.FullName, g2.Value));
                if (f1.Exists)
                    _referencedProjects.Add(g1.Value, f1);

            }

        }


        public TContext GetContext(string name)
        {

            TContext context = null;

            if (!contexts.TryGetValue(name, out context))
            {
                context = new TContext();
                context.Name = name;
                context.Solution = this;
                context.DefaultProject = GetProject(name);
                context.Configuration = Configuration;
                contexts.Add(name, context);
                context.Intialize();
            }

            return context;
        }



    }
}
